/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.evaluation;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.utils.DataUtils;
import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.common.constant.evaluation.ContentNode;
import cn.ac.nercis.pes.common.constant.evaluation.ReviewRole;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.model.dto.evaluation.content.proof.*;
import cn.ac.nercis.pes.model.dto.evaluation.proof.EvaluatorQualificationDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationContentRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluatorQualificationRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluatorRepository;
import cn.ac.nercis.pes.repository.dal.manage.TestedUnitRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.mapper.evaluation.EvaluationProofMapper;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluatorDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluatorQualificationDO;
import cn.ac.nercis.pes.repository.model.project.ProjectDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.service.manage.EvaluationUnitService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 密评证明记录信息服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record ProofService(EvaluationContentService evaluationContentService,
                           EvaluationContentRepository evaluationContentRepository,
                           SystemRepository systemRepository,
                           EvaluatorRepository evaluatorRepository,
                           EvaluatorQualificationRepository evaluatorQualificationRepository,
                           TestedUnitRepository testedUnitRepository,
                           EvaluationUnitService evaluationUnitService) {
    private final static String SCHEME_NAME = "商用密码应用安全性评估方案";

    @Autowired
    public ProofService {
    }

    public DelegateDTO findDelegate(String systemId){
        this.evaluationContentService.repairDuplicateData(systemId, ContentNode.PROOF_DELEGATE);
        var unit= systemRepository.findById(systemId)
                .map(SystemDO::getProject)
                .map(p->{
                    var entrustedUnit = p.getRequesterUnit();
                    return StringUtils.isBlank(entrustedUnit)?p.getTestedUnit():entrustedUnit;
                })
                .orElse(StringUtils.EMPTY);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.PROOF_DELEGATE))
                .map(data -> {
                    var delegate = JsonUtils.fromJson(data.getData(), DelegateDTO.class);
                    if(Objects.nonNull(delegate)){
                        delegate.setUnit(unit);
                    }
                    return delegate;
                })
                .orElse(DelegateDTO.builder().unit(unit).build());
    }

    public synchronized Result<String> saveDelegate(String systemId, DelegateDTO data){
        return evaluationContentService.saveEvaluationContent(systemId, ContentNode.PROOF_DELEGATE,
                JsonUtils.toJson(data));
    }

    public ActivityDTO findActivity(String systemId){
        this.evaluationContentService.repairDuplicateData(systemId, ContentNode.PROOF_ACTIVITY);
        var evaluator = this.evaluatorRepository.findAllBySystemId(systemId)
                .stream()
                .map(EvaluatorDO::getName)
                .collect(Collectors.joining(BaseConstants.WORD_MULTI_JOIN_CHAR));
        var evaluationUnit =evaluationUnitService.findEvaluationUnit().getName();
        var testedUnitId= systemRepository.findById(systemId)
                .map(SystemDO::getProject)
                .map(ProjectDO::getTestedUnitId)
                .orElse(StringUtils.EMPTY);
        var testedUnit = testedUnitRepository.findById(testedUnitId).orElse(null);

        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.PROOF_ACTIVITY))
                .map(data -> {
                    var activity = JsonUtils.fromJson(data.getData(), ActivityDTO.class);
                    Objects.requireNonNull(activity).setUnitTicket(evaluationUnit);
                    return activity;
                })
                .orElse(ActivityDTO.builder()
                        .personTicket(evaluator)
                        .unitTicket(evaluationUnit)
                        .unitRecord(Objects.nonNull(testedUnit) ? testedUnit.getName() : StringUtils.EMPTY)
                        .addressRecord(Objects.nonNull(testedUnit) ? testedUnit.getAddress() : StringUtils.EMPTY)
                        .build());
    }

    public synchronized Result<String> saveActivity(String systemId, ActivityDTO data){
        return evaluationContentService.saveEvaluationContent(systemId, ContentNode.PROOF_ACTIVITY,
                JsonUtils.toJson(data));
    }

    public QualityDTO findQuality(String systemId){
        this.evaluationContentService.repairDuplicateData(systemId, ContentNode.PROOF_QUALITY);
        var systemDO = this.systemRepository.findById(systemId).orElse(null);
        var defaultName = SCHEME_NAME;
        if(Objects.nonNull(systemDO)){
            defaultName = systemDO.getName().concat(SCHEME_NAME);
        }
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.PROOF_QUALITY))
                .map(data -> JsonUtils.fromJson(data.getData(), QualityDTO.class))
                .orElse(QualityDTO.builder().nameScheme(defaultName).build());
    }

    public synchronized Result<String> saveQuality(String systemId, QualityDTO data){
        return evaluationContentService.saveEvaluationContent(systemId, ContentNode.PROOF_QUALITY,
                JsonUtils.toJson(data));
    }

    public GradingDTO findGrading(String systemId){
        this.evaluationContentService.repairDuplicateData(systemId, ContentNode.PROOF_GRADING);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.PROOF_GRADING))
                .map(data -> JsonUtils.fromJson(data.getData(), GradingDTO.class))
                .orElse(GradingDTO.builder().build());
    }

    public synchronized Result<String> saveGrading(String systemId, GradingDTO data){
        return evaluationContentService.saveEvaluationContent(systemId, ContentNode.PROOF_GRADING,
                JsonUtils.toJson(data));
    }

    public EvaluatorProveDTO findEvaluatorProve(String systemId){
        this.evaluationContentService.repairDuplicateData(systemId, ContentNode.PROOF_EVALUATOR);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.PROOF_EVALUATOR))
                .map(data -> JsonUtils.fromJson(data.getData(), EvaluatorProveDTO.class))
                .orElse(EvaluatorProveDTO.builder().build());
    }

    public synchronized Result<String> saveEvaluatorProve(String systemId, EvaluatorProveDTO data){
        return evaluationContentService.saveEvaluationContent(systemId, ContentNode.PROOF_EVALUATOR,
                JsonUtils.toJson(data));
    }

    public List<EvaluatorQualificationDTO> findEvaluatorQualifications(String systemId){
        var query = this.evaluatorQualificationRepository.findAllBySystemId(systemId)
                .stream()
                .map(EvaluationProofMapper.INSTANCE::toDTO)
                .toList();
        return CollectionUtils.isNotEmpty(query) ? query : defaultEvaluatorQualifications(systemId);
    }

    public List<EvaluatorQualificationDTO> defaultEvaluatorQualifications(String systemId){
        var defaultData= this.evaluatorRepository.findAllBySystemId(systemId)
                .stream()
                .map(eval-> EvaluatorQualificationDO.builder()
                        .systemId(systemId)
                        .name(eval.getName())
                        .role(ReviewRole.fromEvaluatorRole(eval.getRole()).getName())
                        .build())
                .toList();
        if(CollectionUtils.isNotEmpty(defaultData)){
            this.evaluatorQualificationRepository.saveAllAndFlush(defaultData);
            return findEvaluatorQualifications(systemId);
        }else{
            return Collections.emptyList();
        }
    }

    public Result<EvaluatorQualificationDTO> saveEvaluatorQualification(EvaluatorQualificationDTO evaluator){
        if(StringUtils.isEmpty(evaluator.getName())){
            evaluator.setName("");
        }
        if(StringUtils.isEmpty(evaluator.getRole())){
            evaluator.setRole("");
        }
        try{
            if(StringUtils.isEmpty(evaluator.getId())){
                return createEvaluatorQualification(evaluator);
            }else{
                return modifyEvaluatorQualification(evaluator);
            }
        }catch (Exception e){
            log.error("保存密评人员资格情况信息出错：",e);
            return Result.failed(ResultCode.FAIL);
        }
    }

    public synchronized Result<String> batchSaveEvaluatorQualification(List<EvaluatorQualificationDTO> data){
        Result<String> result = Result.success();
        for(EvaluatorQualificationDTO item : data){
            Result<EvaluatorQualificationDTO> itemResult = saveEvaluatorQualification(item);
            if(!itemResult.isSuccess()){
                result = Result.failed(ResultCode.FAIL);
                break;
            }
        }
        return result;
    }

    public Result<String> deleteEvaluatorQualification(String id){
        try{
            return evaluatorQualificationRepository.findById(id)
                    .map(evaluatorDO -> {
                        evaluatorQualificationRepository.delete(evaluatorDO);
                        return evaluatorDO;
                    })
                    .map(EvaluatorQualificationDO::getId)
                    .map(Result::success)
                    .orElse(Result.failed());
        }catch (Exception ex){
            log.error("删除密评人员资格情况信息出错：",ex);
            return Result.failed();
        }
    }

    public Result<String> batchDeleteEvaluatorQualification(Set<String> ids){
        Result<String> result = Result.success();
        for(String id : ids){
            Result<String> itemResult = deleteEvaluatorQualification(id);
            if(!itemResult.isSuccess()){
                result = Result.failed(ResultCode.FAIL);
                break;
            }
        }
        return result;
    }

    private Result<EvaluatorQualificationDTO> createEvaluatorQualification(EvaluatorQualificationDTO evaluator){
        return Optional.of(EvaluationProofMapper.INSTANCE.toDO(evaluator))
                .map(e->{
                    e.setCreateTime(LocalDateTime.now());
                    return e;
                })
                .map(evaluatorQualificationRepository::save)
                .map(EvaluationProofMapper.INSTANCE::toDTO)
                .map(Result::success)
                .orElse(Result.failed());
    }

    private Result<EvaluatorQualificationDTO> modifyEvaluatorQualification(EvaluatorQualificationDTO evaluator){
        var source = this.evaluatorQualificationRepository.findById(evaluator.getId()).orElse(null);
        if(Objects.isNull(source)){
            return Result.failed(ResultCode.NOT_FOUND);
        }
        return Optional.of(source)
                .map(s->{
                    s.setName(evaluator.getName());
                    s.setRole(evaluator.getRole());
                    s.setPassDate(evaluator.getPassDate());
                    s.setModifyTime(LocalDateTime.now());
                    return s;
                })
                .map(evaluatorQualificationRepository::save)
                .map(EvaluationProofMapper.INSTANCE::toDTO)
                .map(Result::success)
                .orElse(Result.failed(ResultCode.FAIL));
    }


}